package com.rabbit.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.rabbit.api.RemoteApiService;
import com.rabbit.constant.DeviceType;
import com.rabbit.constant.ResultCodeEnum;
import com.rabbit.constant.SecurityConstant;
import com.rabbit.db.entity.LoginLog;
import com.rabbit.db.entity.User;
import com.rabbit.exception.BusinessException;
import com.rabbit.executor.AsyncTask;
import com.rabbit.mapper.LoginLogMapper;
import com.rabbit.mapper.UserMapper;
import com.rabbit.redis.constant.RedisKeyConstant;
import com.rabbit.redis.service.RedisService;
import com.rabbit.service.LoginService;
import com.rabbit.service.OssService;
import com.rabbit.util.IpUtil;
import com.rabbit.vo.req.UserLoginReq;
import com.rabbit.vo.res.LoginRes;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author zjw
 * @date 2022/1/13 14:32
 */
@Service
public class LoginServiceImpl implements LoginService {

    @Resource
    private RedisService redisService;

    @Resource
    private RemoteApiService remoteApiService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private LoginLogMapper loginLogMapper;

    @Resource
    private OssService ossService;

    @Override
    public LoginRes login(UserLoginReq userLoginReq, HttpServletRequest request) throws BusinessException {
        String validCode = redisService.get(RedisKeyConstant.VERIFY_CODE_IMG_KEY + userLoginReq.getDeviceId());
        if (ObjectUtils.isEmpty(validCode)) {
            throw new BusinessException(ResultCodeEnum.USER_VALID_CODE_EXPIRE_ERROR);
        } else if (!validCode.equals(userLoginReq.getValidCode())) {
            throw new BusinessException(ResultCodeEnum.USER_VALID_CODE_NOT_EQUAL_ERROR);
        }
        User user = User.builder().loginAccount(userLoginReq.getLoginAccount()).build();
        user = userMapper.selectOne(user);
        if (user != null) {
            if (!user.getIsEnabled()) {
                throw new BusinessException(ResultCodeEnum.USER_DISABLED_ERROR);
            }
            // 加密对比
            String password = DigestUtil.md5Hex(user.getSalt() + userLoginReq.getPassword());
            if (Objects.equals(password, user.getPassword())) {
                // PC登录
                StpUtil.login(user.getUserId(), DeviceType.PC);
                SaTokenInfo saTokenInfo = StpUtil.getTokenInfo();
                SaSession tokenSession = StpUtil.getTokenSession();
                tokenSession.set(SecurityConstant.LOGIN_USER, user);
                // 记录登录日志
                insertLoginLog(user);
                LoginRes loginRes = LoginRes.builder().build();
                BeanUtils.copyProperties(user, loginRes);
                loginRes.setAvatar(ossService.packageAvatarUrl(loginRes.getAvatar()));
                loginRes.setCreateTime(DateUtil.formatDateTime(user.getCreateTime()));
                loginRes.setToken(saTokenInfo.getTokenValue());
                return loginRes;
            } else {
                throw new BusinessException(ResultCodeEnum.USER_PASSWORD_ERROR);
            }
        } else {
            throw new BusinessException(ResultCodeEnum.USER_PASSWORD_ERROR);
        }
    }

    /**
     * 记录登录日志
     *
     * @param user 用户信息
     */
    private void insertLoginLog(User user) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        String uaStr = request.getHeader("User-Agent");
        UserAgent userAgent = UserAgentUtil.parse(uaStr);
        LoginLog loginLog = LoginLog.builder().build();
        loginLog.setLoginAccount(user.getLoginAccount());
        loginLog.setLoginTime(DateUtil.date());
        loginLog.setBrowser(userAgent.getBrowser().getName());
        loginLog.setLoginIp(IpUtil.getClientIP(request));
        loginLog.setLoginLocation(remoteApiService.getLoginLocation(loginLog.getLoginIp()));
        loginLog.setOs(userAgent.getOs().getName());
        AsyncTask.COMMON_EXECUTOR.execute(() -> loginLogMapper.insertSelective(loginLog));
    }
}
